using BepuUtilities.Collections;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace BepuUtilities.Memory
{
    /// <summary>
    /// 跨越非托管内存区域。
    /// </summary>
    /// <typeparam name="T">跨区公开的内存类型。</typeparam>
    public unsafe struct Buffer<T> where T : unmanaged
    {
        public T* Memory;
        internal int length;
        // 我们主要对x64感兴趣,所以内存+长度是12字节。无论如何出于对齐原因,该结构将/应该被填充到16字节,
        // 因此,利用最后4个字节来加速从池中获取原始缓冲区的情况(基本上总是如此)是一个很好的选择。

        /// <summary>
        /// 由源设置的原始缓冲区的实现特定标识符。如果从BufferPool中获取,则ID表示从中获取它的电源池中的索引。
        /// </summary>
        public int Id;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Buffer(void* memory, int length, int id = -1)
        {
            Memory = (T*)memory;
            this.length = length;
            Id = id;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref T Get(byte* memory, int index)
        {
            return ref Unsafe.Add(ref Unsafe.As<byte, T>(ref *memory), index);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref T Get(ref RawBuffer buffer, int index)
        {
            Debug.Assert(index >= 0 && index * Unsafe.SizeOf<T>() < buffer.Length, "Index out of range.");
            return ref Get(buffer.Memory, index);
        }

        /// <summary>
        /// 获取对给定索引处的元素的引用。
        /// </summary>
        /// <param name="index">要获取其引用的元素的索引。</param>
        /// <return>对给定索引处的元素的引用。</return>
        public ref T this[int index]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                Debug.Assert(index >= 0 && index < length, "Index out of range.");
                return ref Memory[index];
            }
        }

        /// <summary>
        /// 获取指向给定索引处的元素的指针。
        /// </summary>
        /// <param name="index">要检索其指针的元素的索引。</param>
        /// <return>指向给定索引处的元素的指针。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T* GetPointer(int index)
        {
            Debug.Assert(index >= 0 && index < Length, "Index out of range.");
            return Memory + index;
        }

        /// <summary>
        /// 创建缓冲区内存子集的视图。
        /// </summary>
        /// <param name="start">开始切片缓冲区的索引。</param>
        /// <param name="count">要包括在切片缓冲区中的元素数。</param>
        /// <return>跨越原始缓冲区的指定子集的缓冲区。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Buffer<T> Slice(int start, int count)
        {
            ValidateRegion(start, count);
            return new Buffer<T>(Memory + start, count, Id);
        }

        /// <summary>
        /// 从第一个索引开始,创建缓冲区内存子集的视图。
        /// </summary>
        /// <param name="count">要包括在切片缓冲区中的元素数。</param>
        /// <return>跨越原始缓冲区的指定子集的缓冲区。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Buffer<T> Slice(int count)
        {
            ValidateRegion(0, count);
            return new Buffer<T>(Memory, count, Id);
        }


        /// <summary>
        /// 创建缓冲区内存子集的视图。
        /// </summary>
        /// <param name="start">开始切片缓冲区的索引。</param>
        /// <param name="count">要包括在切片缓冲区中的元素数。</param>
        /// <param name="slated">跨越原始缓冲区的指定子集的缓冲区。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Slice(int start, int count, out Buffer<T> sliced)
        {
            ValidateRegion(start, count);
            sliced = new Buffer<T>(Memory + start, count, Id);
        }

        /// <summary>
        /// 从第一个索引开始,创建缓冲区内存子集的视图。
        /// </summary>
        /// <param name="count">要包括在切片缓冲区中的元素数。</param>
        /// <param name="slated">跨越原始缓冲区的指定子集的缓冲区。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Slice(int count, out Buffer<T> sliced)
        {
            ValidateRegion(0, count);
            sliced = new Buffer<T>(Memory, count, Id);
        }

        /// <summary>
        /// 获取类型化元素中的缓冲区长度。
        /// </summary>
        public int Length
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get { return length; }
        }

        /// <summary>
        /// 获取缓冲区是否引用非空内存。
        /// </summary>
        public bool Allocated
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return Memory != null;
            }
        }

        /// <summary>
        /// 将缓冲区的内存清零。
        /// </summary>
        /// <param name="start">缓冲区中的开始位置。</param>
        /// <param name="count">要清除的超出起始索引的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Clear(int start, int count)
        {
            ValidateRegion(start, count);
            Unsafe.InitBlockUnaligned(Memory + start, 0, (uint)(count * Unsafe.SizeOf<T>()));
        }

        /// <summary>
        /// 将缓冲区数据复制到另一个缓冲区。
        /// </summary>
        /// <param name="sourceStart">源缓冲区中的起始索引。</param>
        /// <param name="target">要复制到的目标缓冲区。</param>
        /// <param name="targetStart">目标缓冲区中的起始索引。</param>
        /// <param name="count">要从源缓冲区复制到目标缓冲区的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void CopyTo(int sourceStart, in Buffer<T> target, int targetStart, int count)
        {
            SpanHelper.Copy(this, sourceStart, target, targetStart, count);
        }

        /// <summary>
        /// 将缓冲区数据复制到另一个缓冲区。
        /// </summary>
        /// <param name="sourceStart">源缓冲区中的起始索引。</param>
        /// <param name="target">要复制到的目标缓冲区。</param>
        /// <param name="targetStart">目标缓冲区中的起始索引。</param>
        /// <param name="count">要从源缓冲区复制到目标缓冲区的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void CopyTo(int sourceStart, Span<T> target, int targetStart, int count)
        {
            SpanHelper.Copy(this, sourceStart, target, targetStart, count);
        }

        /// <summary>
        /// 将跨区数据复制到此缓冲区。
        /// </summary>
        /// <param name="source">要从中复制的源范围。</param>
        /// <param name="sourceStart">源缓冲区中的起始索引。</param>
        /// <param name="targetStart">目标缓冲区中的起始索引。</param>
        /// <param name="count">要从源缓冲区复制到目标缓冲区的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void CopyFrom(Span<T> source, int sourceStart, int targetStart, int count)
        {
            SpanHelper.Copy(source, sourceStart, this, targetStart, count);
        }

        /// <summary>
        /// 将跨区数据复制到此缓冲区。
        /// </summary>
        /// <param name="source">要从中复制的源范围。</param>
        /// <param name="sourceStart">源缓冲区中的起始索引。</param>
        /// <param name="targetStart">目标缓冲区中的起始索引。</param>
        /// <param name="count">要从源缓冲区复制到目标缓冲区的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void CopyFrom(ReadOnlySpan<T> source, int sourceStart, int targetStart, int count)
        {
            SpanHelper.Copy(source, sourceStart, this, targetStart, count);
        }

        /// <summary>
        /// 使用类型的默认比较器获取缓冲区中元素的索引。
        /// </summary>
        /// <param name="element">要在缓冲区中查找的元素。</param>
        /// <param name="start">开始搜索的起始索引。</param>
        /// <param name="count">要扫描到起始索引之外的元素数。</param>
        /// <return>如果找到,则为缓冲区中元素的索引,否则为-1。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int IndexOf(ref T element, int start, int count)
        {
            ValidateRegion(start, count);
            var end = start + count;
            if (SpanHelper.IsPrimitive<T>())
            {
                var defaultComparer = default(PrimitiveComparer<T>);
                for (int i = start; i < end; ++i)
                    if (defaultComparer.Equals(ref this[i], ref element))
                        return i;
                return -1;
            }
            else
            {
                WrapperEqualityComparer<T>.CreateDefault(out var defaultComparer);
                for (int i = start; i < end; ++i)
                    if (defaultComparer.Equals(ref this[i], ref element))
                        return i;
                return -1;
            }
        }

        /// <summary>
        /// 使用类型的默认比较器获取缓冲区中元素的索引。
        /// </summary>
        /// <param name="element">要在缓冲区中查找的元素。</param>
        /// <param name="start">开始搜索的起始索引。</param>
        /// <param name="count">要扫描到起始索引之外的元素数。</param>
        /// <return>如果找到,则为缓冲区中元素的索引,否则为-1。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int IndexOf(T element, int start, int count)
        {
            return IndexOf(ref element, start, count);
        }

        /// <summary>
        /// 获取与提供的谓词匹配的第一个元素的索引。
        /// </summary>
        /// <param name="谓词">用于测试每个元素的谓词。</param>
        /// <param name="start">开始搜索的起始索引。</param>
        /// <param name="count">要扫描到起始索引之外的元素数。</param>
        /// <return>缓冲区中第一个匹配元素的索引(如果有),否则为-1。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int IndexOf<TPredicate>(ref TPredicate predicate, int start, int count) where TPredicate : IPredicate<T>
        {
            ValidateRegion(start, count);
            var end = start + count;
            for (int i = start; i < end; ++i)
                if (predicate.Matches(ref this[i]))
                    return i;
            return -1;
        }

        /// <summary>
        /// 创建包含与缓冲区<typeparamref name="T"/>相同数据的非类型化缓冲区。
        /// </summary>
        /// <return>包含与源缓冲区相同数据的非类型化缓冲区。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public RawBuffer AsRaw()
        {
            RawBuffer buffer;
            buffer.Memory = (byte*)Memory;
            buffer.Length = length * Unsafe.SizeOf<T>();
            buffer.Id = Id;
            return buffer;
        }

        [Conditional("DEBUG")]
        void ValidateRegion(int start, int count)
        {
            Debug.Assert(start >= 0, "The start of a region must be within the buffer's extent.");
            Debug.Assert(start + count <= length, "The end of a region must be within the buffer's extent.");
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static implicit operator Span<T>(in Buffer<T> buffer)
        {
            return new Span<T>(buffer.Memory, buffer.Length);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static implicit operator ReadOnlySpan<T>(in Buffer<T> buffer)
        {
            return new ReadOnlySpan<T>(buffer.Memory, buffer.Length);
        }
    }

}
